using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using DailyPoetryX.AzureStorage.Models;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;

namespace DailyPoetryX.AzureStorage.Services.Implementations {
    /// <summary>
    /// 身份验证服务。
    /// </summary>
    public class AuthenticationService : IAuthenticationService {
        /******** 公开变量 ********/

        /******** 私有变量 ********/

        /// <summary>
        /// 配置管理器。
        /// </summary>
        private IConfigurationManager<OpenIdConnectConfiguration>
            _configurationManager;

        /// <summary>
        /// 身份信息签发者。
        /// </summary>
        private const string Issuer = "https://dailypoetryx.auth0.com/";

        /// <summary>
        /// 身份信息获得者。
        /// </summary>
        private const string Audience = "https://dailypoetryx-azure-storage";

        /// <summary>
        /// Token缓存。
        /// </summary>
        private ITokenCache _tokenCache;

        /******** 继承方法 ********/

        /// <summary>
        /// 验证身份。
        /// </summary>
        public async Task<AuthenticationResult> AuthenticateAsync(
            string token) {
            var name = await _tokenCache.GetAsync(token);
            if (name != null) {
                return new AuthenticationResult {Name = name, Passed = true};
            }

            try {
                var openIdConfig =
                    await _configurationManager.GetConfigurationAsync(
                        CancellationToken.None);

                TokenValidationParameters validationParameters =
                    new TokenValidationParameters {
                        ValidIssuer = Issuer,
                        ValidAudiences = new[] {Audience},
                        IssuerSigningKeys = openIdConfig.SigningKeys
                    };

                JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
                var validationResult = handler.ValidateToken(token,
                    validationParameters, out _);
                name = validationResult.Claims
                    .FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value;
            } catch (SecurityTokenSignatureKeyNotFoundException e) {
                _configurationManager.RequestRefresh();
                return new AuthenticationResult {
                    Message =
                        $"SecurityTokenSignatureKeyNotFoundException: {e.Message}"
                };
            } catch (SecurityTokenException e) {
                return new AuthenticationResult {
                    Message =
                        $"SecurityTokenException: {e.Message}"
                };
            }

            if (name == null) {
                return new AuthenticationResult {Message = "Unknown name."};
            }

            await _tokenCache.SetAsync(token, name);
            return new AuthenticationResult {Name = name, Passed = true};
        }

        /******** 公开方法 ********/

        /// <summary>
        /// 身份验证服务。
        /// </summary>
        /// <param name="tokenCache">Token缓存。</param>
        public AuthenticationService(ITokenCache tokenCache) {
            var documentRetriever = new HttpDocumentRetriever {
                RequireHttps = Issuer.StartsWith("https://")
            };
            _configurationManager =
                new ConfigurationManager<OpenIdConnectConfiguration>(
                    $"{Issuer}.well-known/openid-configuration",
                    new OpenIdConnectConfigurationRetriever(),
                    documentRetriever);

            _tokenCache = tokenCache;
        }

        /******** 私有方法 ********/
    }
}